﻿/* 
 * Copyright 2010 The Apache Software Foundation.
 *
 * Licensed under the Apache License, Version 2.0 (the "License")
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Ngilead.Core.Exception;
using System.Reflection;
using System.Collections;
using Ngilead.Core.Store;

namespace Ngilead.Core.Replicator.Merge
{
    /// <summary>Property filter for merge operation</summary>
    /// <author>bruno.marchesson</author>
    class MergePropertyFilter : IPropertyFilter
    {
        /*----------------------------------------------------------------------------------------*/
        #region Properties

        /// <summary>The associated persistence util</summary>
        public IPersistenceUtil PersistenceUtil { get; set; }
        
        /// <summary>Proxy store attribute</summary>
        public IProxyStore ProxyStore { get; set; }

        /// <summary>The current bean transformer</summary>
        public IBeanTransformer BeanTransformer { get; set; }
        #endregion
        /*----------------------------------------------------------------------------------------*/

        /*----------------------------------------------------------------------------------------*/
        #region Constructor
        /// <summary> Constructor</summary>
        public MergePropertyFilter(IBeanTransformer beanTransformer, 
                                   IPersistenceUtil persistenceUtil, 
                                   IProxyStore proxyStore)
        {
            BeanTransformer = beanTransformer;
            PersistenceUtil = persistenceUtil;
            ProxyStore = proxyStore;
        }
        
        #endregion
        /*----------------------------------------------------------------------------------------*/

        /*----------------------------------------------------------------------------------------*/
        #region IPropertyFilter Membres

        public bool ShouldPopulate(string propertyName, object cloneBean, object persistentBean)
        {
            try
		    {
            //  Do not merge LightEntity fields
            //
			    if ((Constants.PROXY_INFORMATIONS == propertyName) ||
				    (Constants.INITIALIZATION_MAP == propertyName))
			    {
				    return false;
			    }
    			
		    //	Get proxy informations
		    //
			    Dictionary<string, Object> proxyInformations = 
				    ProxyStore.GetProxyInformations(cloneBean, propertyName);
                
                if (proxyInformations == null)
                {
                //	No proxy informations : just populate the property
                //
                    return true;
                }


		    //	Get clone value
		    //
                
                Object cloneValue = ReadPropertyValue(cloneBean, propertyName);
    			    	
                // Null value : can be a proxy
                if ((IsNullValue(cloneValue)) &&
                    (IsInitialized(proxyInformations) == false))
                {
                //	Unitialized property merge
                //
                    if (PersistenceUtil.IsDenotingPersistentDictionary(proxyInformations))
                    {
                    //	Set dictionary proxy
                    //
                        Object persistentDictionary = PersistenceUtil.CreatePersistentDictionary(persistentBean, proxyInformations, null);
                        WritePropertyValue(persistentBean, persistentDictionary,
                                           propertyName);
                    } 
                    else if (PersistenceUtil.IsDenotingPersistentCollection(proxyInformations))
                    {
                    //	Set collection proxy
                    //
                        Object persistentCollection = PersistenceUtil.CreatePersistentCollection(persistentBean, proxyInformations, null);
                        WritePropertyValue(persistentBean, persistentCollection, 
                                           propertyName);
                    }
                    else
                    {
                    //	Set an entity proxy
                    //
                        Object proxy = PersistenceUtil.CreateEntityProxy(proxyInformations);
                        if (proxy != null)
                        {
                            WritePropertyValue(persistentBean, proxy,
                                               propertyName);
                        }
                    }
    				
                //	Skip in-depth population
                //
                    return false;
                }
                else if (cloneValue is ICollection)
                {
                //	Replace by persistent collection 
                //  (we will need proxy information later -> see MergeCollectionReplicator)
                //
                    BeanTransformer.CurrentProxyInformations = proxyInformations;

                }
    			
			    return true;
            }
		    catch (System.Exception e)
		    {
			    throw new CloneException(e.Message, e);
		    }
        }

        #endregion
        /*----------------------------------------------------------------------------------------*/

        /*----------------------------------------------------------------------------------------*/
        #region Internal methods

        /// <summary>
        /// Read a property value, even if it has a private getter
        /// </summary>
        /// <param name="bean">the source bean</param>
        /// <param name="propertyName">the property name</param>
        /// <returns>the value of the property</returns>
        private Object ReadPropertyValue(Object bean, string propertyName)
        {
        //  Get property
        //
            PropertyInfo property = bean.GetType().GetProperty(propertyName);

        //  Get associated value
        //
            return property.GetValue(bean, null);
        }

        /**
        * Write a property value, even if it has a private setter
        */
        private void WritePropertyValue(Object bean, Object value,
                                        String propertyName)
        {
        //  Get property
        //
            PropertyInfo property = bean.GetType().GetProperty(propertyName);

        //  Set associated value
        //
            property.SetValue(bean, value, null);
        }
    	

        /// <summary>
        /// Inidicates if the property was initialized or not, based on its proxy informations.
        /// </summary>
        /// <param name="proxyInfo"></param>
        /// <returns></returns>
        private bool IsInitialized(Dictionary<string, Object> proxyInfo)
        {
	        if ((proxyInfo != null) &&
                (proxyInfo.Keys.Contains(Constants.INITIALISED)))
	        {

		        bool initialized = (bool) proxyInfo[Constants.INITIALISED];
		        return initialized; 
	        }
    		
        //	The property has no proxy info or it does not
        //	contains 'initialized' field
        //
	        return true;
        }

	   
	    /// <summary>Indicates if the argument value must be considered as null
        /// (empty collections or map are also considered as null)
	    /// </summary>
	    private bool IsNullValue(Object value)
	    {
		    if (value == null)
		    {
			    return true;
		    }
		    else if (value is ICollection)
		    {
			    return ((ICollection) value).Count == 0;
		    }
		   
		    return false;
	    }

        #endregion
        /*----------------------------------------------------------------------------------------*/
    }
}
